/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package agorithms;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.*;
import java.text.DecimalFormat;

/**
 *
 * @author hsb
 */
public class TopNItemBased {
    //k most similar items to item j ( 10 <= k <=30)

//    int k = 10;
    
    double alpha = 0.5;
    //the number of items to be recommended
    int N = 10;
    Utils util = new Utils();

    public float computeAverage(float itemUser[][], int user, int maxItem, float result[]) {
        float average = 0;
        int dem = 0;
        for (int i = 0; i < maxItem; i++) {
            if (itemUser[user][i] != 0) {
                dem++;
                average += itemUser[user][i];
            }
        }
//        for(int i=0; i< maxItem; i++)
//        {
//            if(itemUser[i][user] != 0) {
//                dem++;
//                average += itemUser[i][user];
//            }
//        }
        result[0] = average;
        result[1] = dem;
        return (float) average;
    }

    public float computeVariance(float itemUser[][], int user, float average, int maxItem) {
        float variance = 0;
        for (int i = 0; i < maxItem; i++) {
            if (itemUser[user][i] != 0) {
                variance += (float) (itemUser[user][i] - average) * (itemUser[user][i] - average);
            }
        }
//        for(int i=0; i< maxUser; i++)
//        {
//            if(itemUser[i][user] != 0) { 
//                variance += (float) (itemUser[i][user] - average)*(itemUser[i][user] - average);
//            }
//        }
        return variance;
    }

    public void normalizeRating(float itemUser[][], int maxUser, int maxItem) {
        float average = 0;
        double temp = 0;
        float variance = 0;
        float result[] = new float[2];
        for (int i = 0; i < maxUser; i++) {
            average = computeAverage(itemUser, i, maxItem, result);
            variance = computeVariance(itemUser, i, average, maxItem);
            //System.out.print(" i " + i + " avg = "+average + "\n");
            for (int j = 0; j < maxItem; j++) {
                if (itemUser[i][j] != 0) {
//                    itemUser[i][j] = (float) itemUser[i][j] / average;
                    //System.out.print(" i " + i + " va= " + itemUser[i][j] + " avg = "+average + "\n");
                    itemUser[i][j] = (float) (( Math.abs(itemUser[i][j] - average))/(Math.sqrt(variance))); ///
//                    //itemUser[i][j] = (itemUser[i][j] - 1) / (5-1);
                    //System.out.print(" i " + i + " va= " + itemUser[i][j] + " avg = "+average + "\n");
                }
            }
        }
    }
    
    //public void normalizeSimilarity()

    //build model (page 7 - Item-Based Top-N Recommendation Algorithms.pdf)
    //itemUser: itemUser matrix 
    //maxUser: the number of user of itemUser matrix
    //maxItem: the number of item of itemUser matrix
    public double[][] buildModel(float[][] itemUser, int k, int maxUser, int maxItem) throws FileNotFoundException {
        // M : item-item matrix 
        double M[][] = new double[maxItem][maxItem];
        Pearson p = new Pearson();
        double max = 0;
        double rowTotal = 0;
        float maxVal = 0;
//        for (int i = 0; i < maxUser; i++) {
//            //System.out.print(" i " + i + " avg = "+average + "\n");
//            for (int j = 0; j < maxItem; j++) {
//                if (itemUser[i][j] != 0) {
//                    System.out.print(" i " + i + "vs j = " + j + " = "+ itemUser[i][j] + "\n");
//                    if( itemUser[i][j] > maxVal) maxVal = itemUser[i][j];
//                }
//            }
//        }
//        System.out.print("max = " + maxVal);
        // line 1 - page 7 - Item-Based Top-N Recommendation Algorpithms.pdf
        for (int j = 0; j < maxItem; j++) {
            for (int i = 0; i < maxItem; i++) {
                if (i != j) {
//                    M[i][j] = p.computeCosine(j, i, itemUser, maxUser, maxItem);

//                    M[i][j] = p.computeItemBasedPearson(i, j, itemUser, maxUser, maxItem);
                    M[i][j] = computeConditionalProbability(itemUser, j, i, maxUser, maxItem);
//                    System.out.print(i + "  vs " + j + " = "+M[i][j] +"\n");
                } else {
                    M[i][j] = 0;
                }
            }
            //break;
        }

//        for(int j=0; j< maxItem; j++)
//        {
//            for(int i=0; i< maxItem; i++)
//            {
//                if(M[i][j] !=0 ) 
//                {
//                    rowTotal += M[i][j]*M[i][j];
//                    if(j !=0 && j<maxItem-1) {
//                        M[i][j-1] = (double) ( (M[i][j-1]*M[i][j]) / (rowTotal));
//                    }
//                    if(j==maxItem-1) {
//                        M[i][j] = (double) ((M[i][j]*M[i][j]) / (rowTotal));
//                    }
//                }
////                if(M[i][j] > 0 )
////                    M[i][j] = ((M[i][j]) / Math.sqrt(M[i][j]*M[i][j] + 0.3));
////                else if (M[i][j] < 0 )
////                    M[i][j] = ((M[i][j]) / Math.sqrt(M[i][j]*M[i][j] + 0.7));
//                
////                    System.out.print(i + " vs " + j + " = "+M[i][j] +"\n");
//            }
//            if (j!=0) {
//                rowTotal = 0;
//            }
//            //break;
//        }

        // line 2 - page 7 - Item-Based Top-N Recommendation Algorithms.pdf

        for (int j = 0; j < maxItem; j++) {
            // get the kth largest values in M matrix
            int dem = 0;
            max = util.getKthLargest(M, k, j, maxItem);
            for (int i = 0; i < maxItem; i++) {
                if (M[i][j] < max) {
                    M[i][j] = 0;
                } else {
                    dem++;
                }
            }
            //System.out.print(j+ " = " + dem + " max= " + max + "\n");
            dem = 0;
        }

        return M;
    }

    // ApplyModel - page 11 - Item-Based Top-N Recommendation Algorithms.pdf
    public double[] applyModel(double[][] M, double[] U, int N, int maxItem) {
        // X: m × 1 vector whose non-zero entries correspond to the top-N items that were recommended.
        double X[] = new double[maxItem];
        double max = 0;

        // line 1 - page 11 - Item-Based Top-N Recommendation Algorithms.pdf
        X = util.multiplyMatrix(M, U, maxItem);

        // line 2 - page 11 - Item-Based Top-N Recommendation Algorithms.pdf
        for (int j = 0; j < maxItem; j++) {
            if (U[j] == 1) {
                X[j] = 0;
            }
        }

        // get the kth largest values in M matrix
        max = util.getKthLargest(X, N, maxItem);

        // line 3 - page 11 - Item-Based Top-N Recommendation Algorithms.pdf
        for (int j = 0; j < maxItem; j++) {
            if (X[j] < max) {
                X[j] = 0;
            }
        }
        return X;
    }

    // get m × 1 vector U that stores the items that have already been purchased by the active user
    // The active user’s information in vector U is encoded by 
    // setting Ui = 1 if the user has purchased the ith item and zero otherwise
    public double[] getItemPurchased(float[][] itemUser, int user, int maxItem) {
        double U[] = new double[maxItem];
        for (int i = 0; i < maxItem; i++) {
            if (itemUser[user][i] != 0) {
                U[i] = 1;
            } else {
                U[i] = 0;
            }
        }
        return U;
    }

    public double computeCosine(int item1, int item2, float itemUser[][], int maxUser, int maxItem) throws FileNotFoundException
    {
        double cosine = 0;
        double tu = 0;
        double mau = 0;

        try {
          float temp1 = 0;
          float temp2 = 0;
          int rate1 = 0;
          int rate2 = 0;
          for(int j=0; j< maxUser; j++)
          {
//              for(int j=0; j< maxItem; j++)
              //check user purchased both item1 and item2
              if(itemUser[j][item1] !=0) 
              {
                  rate1 = 1;
              } else rate1 = 0;
              if(itemUser[j][item2] !=0) 
              {
                  rate2 = 1; 
              } else rate2 = 0;
              temp1 += rate1;
              temp2 += rate2;
              tu += rate1 * rate2;
          }
              mau = Math.sqrt(temp1) * Math.sqrt(temp2);
              cosine = tu / mau;
         if(tu == 0 || mau == 0)
              cosine = 0;
//         DecimalFormat df = new DecimalFormat(".###");
//         String str= df.format(cosine);
//         cosine = Double.valueOf(str);    
         
        } catch (Exception ioe ) {
          ioe.printStackTrace();
          // nothing else you can do? Just abort for now!
          System.err.println( "[Error] could parse the file" );
        }
        return cosine;
    }
    
    public double computeConditionalProbability(float itemUser[][], int jth, int ith, int maxUser, int maxItem) {
        double result = 0;
        
        double row = 0;
        int freqI = 0;
        int freqJ = 0;

        for (int i = 0; i < maxUser; i++) {
            if (itemUser[i][ith] != 0) {
                freqI++;
            }
            if (itemUser[i][jth] != 0) {
                freqJ++;
            }

            
//            if (itemUser[i][ith] != 0 && itemUser[i][jth] != 0) { //
//                row += itemUser[i][ith];
//            }
            
            //rating normlization
            if (itemUser[i][jth] > 0 ) { //
                row += itemUser[i][ith];
            }

            //not rating normlization
//            if(itemUser[i][ith] != 0 && itemUser[i][jth] != 0) { //
//                    row ++;//= itemUser[i][jth];
//            }

        }

        result = row / (freqJ * Math.pow(freqI, alpha));
        if (row == 0 || freqI == 0 || freqJ == 0) {
            result = 0;
        }
//        DecimalFormat df = new DecimalFormat(".###");
//        String str = df.format(result);
//        result = Double.valueOf(str);


        return result;
    }

    // generate N recommended items for specific user.
    public int[] topN(float[][] itemUser, int user, int maxUser, int maxItem, int N, int k) throws FileNotFoundException {

        double M[][] = new double[maxItem][maxItem];
        double U[] = new double[maxItem];
        double X[] = new double[maxItem];
        int listItem[] = new int[N];
        M = buildModel(itemUser, k, maxUser, maxItem);
        U = getItemPurchased(itemUser, user, maxItem);
        X = applyModel(M, U, N, maxItem);

        // The output of the algorithm is an m × 1 vector x 
        // whose non-zero entries correspond to the top-N items that were recommended
        int item = 0;
        for (int x = 0; x < N; x++) {
            //if(X[x]!=0) 
            {
                item = util.getKthLargestPosition(X, x + 1, maxUser, maxItem);
                listItem[x] = item;
                //System.out.print("Item: " + (x+1) + " Value= " + X[x] + "\n");
            }
        }
        return listItem;
    }

    //
    public void generateTopN(String dataset, int k) {
        int MaxNumber = 2000;
        Utils util = new Utils();
        int maxItem = 0;
        int maxUser = 0;
        int maxUserBase = 0;
        float itemUser[][] = new float[MaxNumber][MaxNumber];
        float temp[][] = new float[MaxNumber][MaxNumber];
        float itemUserTest[][] = new float[MaxNumber][MaxNumber];
        int numUserItemBase[] = new int[2];
        int numUserItemTest[] = new int[2];
        numUserItemTest[0] = 0;
        numUserItemTest[1] = 0;
        //Training set 
        File base = new File("src/data/" + dataset + ".base");
        //read training set file, return itemUser matrix with maxUser row and maxItem column
        // rating = itemUser[user][item]
        itemUser = util.readFile(itemUser, numUserItemBase, base);

        //Testing set
        File test = new File("src/data/" + dataset + ".test");
        // read testing set file, return itemUserTest matrix with maxUser row and maxItem column
        // rating = itemUserTest[user][item]
        itemUserTest = util.readFile(itemUserTest, numUserItemTest, test);

        //get number of user, and number of item in matrix
        maxUser = numUserItemTest[0] + 1;
        maxItem = numUserItemBase[1] + 1;
        maxUserBase = numUserItemBase[0] + 1;
//        maxItem = 1682;
        // result matrix
        double X[] = new double[maxItem];
        // M matrix: item-item
        double M[][] = new double[maxItem][maxItem];
        // U matrix: purchased item
        double U[] = new double[maxItem];

        int item = 0;
        int x = 0;
        try {
            // result file
            File myfile = new File(dataset + ".rowCond");
            FileWriter output = new FileWriter(myfile);
            //build model
            temp = itemUser;
            normalizeRating(itemUser, maxUserBase, maxItem);
            M = buildModel(itemUser, k, maxUserBase, maxItem);

            //with every user, generate N=10 items and save them in myfile.
            for (int i = 0; i < maxUser; i++) {
                //get purchased item from i user.
                U = getItemPurchased(temp, i, maxItem);
                {
                    // return result matrix with N=10 non-zero item
                    X = applyModel(M, U, N, maxItem);
                    //write to file.
                    for (int d = 0; d < N; d++) {
                        //if(X[k]!=0)
                        {
                            item = util.getKthLargestPosition(X, d + 1, maxUser, maxItem);
                            String s = (i + 1) + "\t" + (d + 1) + "\t" + (item + 1) + "\t" + "0";
                            BufferedWriter buffer = new BufferedWriter(output);
                            buffer.write(s);
                            buffer.write("\n");
                            buffer.flush();
                            x++;
                        }
                    }
                    x = 0;
                }
            }
        } catch (Exception ioe) {
            ioe.printStackTrace();
            // nothing else you can do? Just abort for now!
            System.err.println("[Error] could parse the file");
        }
    }

    //compute hit rate.
    public void computeHitRate(String dataset, double eval[]) {
        int MaxNumber = 2000;
        int maxItem = 0;
        int maxUser = 0;
        Utils util = new Utils();
        float[][] test = new float[MaxNumber][MaxNumber];
        float[][] result = new float[MaxNumber][MaxNumber];
        int numUserItem[] = new int[2];
        File base = new File("src/data/" + dataset + ".base");
        //read training set file, return itemUser matrix with maxUser row and maxItem column
        // rating = itemUser[user][item]
        float itemUser[][] = new float[MaxNumber][MaxNumber];
        int numUserItemBase[] = new int[2];
        itemUser = util.readFile(itemUser, numUserItemBase, base);
        //compare between testing set and result set        
        File testArr = new File("src/data/" + dataset + ".test");
        File resultArr = new File(dataset + ".rowCond");
        // read file and return matrix
        test = util.readFile(test, numUserItem, testArr);

        maxUser = numUserItem[0];
        maxItem = numUserItem[1];
//        maxUser = numUserItem[0];
        //maxItem = 1682;
//        System.out.append(maxUser + " vs " + maxItem + "\n");
        result = util.readFile(result, numUserItem, resultArr);

        double HitRate = 0;
        double ARHR = 0;
        int hits = 0;
        double temp = 0;

        //The number of hits is the number of items in the test set 
        //that were also present in the top-N recommended items returned for each user
        for (int i = 0; i < maxUser; i++) {
            for (int j = 0; j < maxItem; j++) {
                if (test[i][j] != 0) {

                    for (int x = 0; x < N; x++) {
                        //compare item in test set and item in result set.
                        //System.out.print(i + " " + (j+1) + " = " + result[i][x] + "\n");
                        if ((j + 1) == result[i][x]) {
                            temp += (double) 1 / (x + 1);
                            hits++;
                            //System.out.print(i+1 + " " + (j+1) + " = " + result[i][x] + " pos = " + (x+1) + "\n");
                            //break;
                        }

                    }
                }
            }
        }

        ARHR = (double) temp / (maxUser * N);
        maxUser = numUserItemBase[0];
        HitRate = (double) hits / (maxUser * N);
        eval[0] = HitRate;
        eval[1] = ARHR;
        
        DecimalFormat df = new DecimalFormat(".###");
        String str = df.format(HitRate);
        HitRate = Double.valueOf(str);
        str = df.format(ARHR);
        ARHR = Double.valueOf(str);
        
//        System.out.print("HitRate == " + HitRate + "\n");
//        System.out.print("ARHR == " + ARHR + "\n");
//        System.out.print("ARHR == " + temp + "\n");
//        System.out.print("Hits == " + hits + "\n");
        
    }
    
    public void computePrecision(String dataset, double eval[]) {
        int MaxNumber = 2000;
        int maxItem = 0;
        int maxUser = 0;
        Utils util = new Utils();
        float[][] test = new float[MaxNumber][MaxNumber];
        float[][] result = new float[MaxNumber][MaxNumber];
        int numUserItem[] = new int[2];
        File base = new File("src/data/" + dataset + ".base");
        //read training set file, return itemUser matrix with maxUser row and maxItem column
        // rating = itemUser[user][item]
        float itemUser[][] = new float[MaxNumber][MaxNumber];
        int numUserItemBase[] = new int[2];
        itemUser = util.readFile(itemUser, numUserItemBase, base);
        //compare between testing set and result set        
        File testArr = new File("src/data/" + dataset + ".test");
        File resultArr = new File(dataset + ".rowCond");
        // read file and return matrix
        test = util.readFile(test, numUserItem, testArr);

        maxUser = numUserItem[0];
        maxItem = numUserItem[1];
//        maxUser = numUserItem[0];
        //maxItem = 1682;
        System.out.append(maxUser + " vs " + maxItem + "\n");
        result = util.readFile(result, numUserItem, resultArr);

        double HitRate = 0;
        double ARHR = 0;
        int hits = 0;
        double temp = 0;
        int tp = 0;
        int fp = 0;
        int fn = 0;
        
        int t = 0;
        //The number of hits is the number of items in the test set 
        //that were also present in the top-N recommended items returned for each user
        for (int i = 0; i < maxUser; i++) {
            for (int j = 0; j < maxItem; j++) {
                if (test[i][j] != 0) {

                    for (int x = 0; x < N; x++) {
                        //compare item in test set and item in result set.
                        //System.out.print(i + " " + (j+1) + " = " + result[i][x] + "\n");
                        if ((j + 1) == result[i][x]) {
                            tp++;
                        } 
                        //tp += t;
//                        fp += (N-t);
                        //if()
                    }
                    
//                    break;
                    
                    
                }
            }
//            System.out.println("t: " + t );
                    t = 0;
        }

        double precison  = 0;
        precison = (double) tp/(maxUser*10);
        System.out.print("TP == " + tp + "\n");
        System.out.print("FP == " + maxUser*10 + "\n");
        System.out.print("Precision == " + precison + "\n");
//        System.out.print("HitRate == " + HitRate + "\n");
    }
}